/** 
 * Allows the valueClasses to be set in MXML, e.g.
 * <signals:Signal id="nameChanged">{[String, uint]}</signals:Signal>
 */
package org.osflash.signals;

import flash.errors.ArgumentError;
import org.osflash.signals.events.IBubbleEventHandler;
import org.osflash.signals.events.IEvent;

class DeluxeSignal extends PrioritySignal {
	public var target(getTarget, setTarget) : Dynamic;

	var _target : Dynamic;
	/**
	 * Creates a DeluxeSignal instance to dispatch events on behalf of a target object.
	 * @param	target The object the signal is dispatching events on behalf of.
	 * @param	valueClasses Any number of class references that enable type checks in dispatch().
	 * For example, new DeluxeSignal(this, String, uint)
	 * would allow: signal.dispatch("the Answer", 42)
	 * but not: signal.dispatch(true, 42.5)
	 * nor: signal.dispatch()
	 *
	 * NOTE: Subclasses cannot call super.apply(null, valueClasses),
	 * but this constructor has logic to support super(valueClasses).
	 */
	public function new(target : Dynamic = null,valueClasses:Array<Dynamic>=null) {
		_target = target;
		if(valueClasses == null)
			valueClasses = [];
		this.valueClasses = valueClasses;
		super(valueClasses);
	}

	/** @inheritDoc */
	public function getTarget() : Dynamic {
		return _target;
	}

	public function setTarget(value : Dynamic) : Dynamic {
		if(value == _target) return;
		removeAll();
		_target = value;
		return value;
	}

	/**
	 * @inheritDoc
	 * @throws ArgumentError <code>ArgumentError</code>: Incorrect number of arguments.
	 * @throws ArgumentError <code>ArgumentError</code>: Value object is not an instance of the appropriate valueClasses Class.
	 */
	override public function dispatch(valueObjects:Array<Dynamic>=null) : Void {
		if(valueObjects == null)
			valueObjects = [];
		// Validate value objects against pre-defined value classes.
		var numValueClasses : Int = _valueClasses.length;
		var numValueObjects : Int = valueObjects.length;
		if(numValueObjects < numValueClasses)  {
			throw new ArgumentError("Incorrect number of arguments. " + "Expected at least " + numValueClasses + " but received " + numValueObjects + ".");
		}
		var i : Int = 0;
		while(i < numValueClasses) {
			// Optimized for the optimistic case that values are correct.
			if(Std.is(valueObjects[i], _valueClasses[i]) || valueObjects[i] == null)  {
				i++;
				continue;
			}
			throw new ArgumentError("Value object <" + valueObjects[i] + "> is not an instance of <" + _valueClasses[i] + ">.");
			i++;
		}
		// Extract and clone event object if necessary.
		var event : IEvent = try cast(valueObjects[0], IEvent) catch(e:Dynamic) null;
		if(event != null)  {
			if(event.target != null)  {
				event = event.clone();
				valueObjects[0] = event;
			}
			event.target = target;
			event.currentTarget = target;
			event.signal = this;
		}
		var slotsToProcess : SlotList = slots;
		while(slotsToProcess.nonEmpty) {
			slotsToProcess.head.execute(valueObjects);
			slotsToProcess = slotsToProcess.tail;
		}

		// Bubble the event as far as possible.
		if(event == null || !event.bubbles) return;
		var currentTarget : Dynamic = target;
		while(currentTarget && Reflect.hasField(currentTarget,"parent") && (null != (currentTarget = Reflect.field(currentTarget,"parent")))) {
			if(Std.is(currentTarget, IBubbleEventHandler))  {
				// onEventBubbled() can stop the bubbling by returning false.
				if(!cast((event.currentTarget = currentTarget), IBubbleEventHandler).onEventBubbled(event)) break;
			}
		}

	}

}

